/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/


#include "MkTimer.h"
#include "MkUtil/MkUtil.h"
#include "MkUtil/MkLog.h"
CMkTimer::CMkTimer()
    : m_WaitEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr))
    , m_NextActiveTime(NowMkTime)
    , m_bRun(FALSE)
    , m_bHasDelete(FALSE)
{

}

Uint32 CMkTimer::StopTimerThread()
{
    LoopBreak();
    m_TimerThread.Stop();
    return NoneError;
}

Uint32 CMkTimer::AddDelayer(const DelayerCallback Cb, Uint32 MilliSecDelay, INOUT MkString& DelaySession)
{
    if (!Cb) {
        return InvalidParams;
    }
    MkDelayerParam Param;
    Param.ActiveTime = NowMkTime + MilliSecDelay * 1000;
    Param.Cb = Cb;
    Param.MilliSecDelay = MilliSecDelay;
    if (DelaySession.empty()) {
        CMkUtil::CreateRandomString(Len16, DelaySession);
    }

    m_DelayerLock.WLock();
    m_MapDelayer[DelaySession] = Param;
    m_DelayerLock.WUnlock();
    if (Param.ActiveTime < m_NextActiveTime) {
        m_NextActiveTime = Param.ActiveTime;
    }
    SetEvent(m_WaitEvent);
    return NoneError;
}

Uint32 CMkTimer::AddTimer(const DelayerCallback Cb, Uint32 MilliSecInterval, INOUT MkString& TimerSession)
{
    if (!Cb) {
        return InvalidParams;
    }
    MkDelayerParam Param;
    Param.ActiveTime = NowMkTime + MilliSecInterval * 1000;
    Param.Cb = Cb;
    Param.MilliSecDelay = MilliSecInterval;
    Param.bTimer = TRUE;
    if (TimerSession.empty()) {
        CMkUtil::CreateRandomString(Len16, TimerSession);
    }

    m_DelayerLock.WLock();
    m_MapDelayer[TimerSession] = Param;
    m_DelayerLock.WUnlock();
    if (Param.ActiveTime < m_NextActiveTime) {
        m_NextActiveTime = Param.ActiveTime;
    }
    SetEvent(m_WaitEvent);
    return NoneError;
}

Uint32 CMkTimer::DeleteTimer(const MkString& TimerSession)
{
    m_bHasDelete = TRUE;
    Uint32 ErrorCode = InvalidSession;
    m_DelayerLock.WLock();
    MkMap<MkString, MkDelayerParam>::iterator it = m_MapDelayer.find(TimerSession);
    if (it != m_MapDelayer.end()) {
        m_MapDelayer.erase(it);
        ErrorCode = NoneError;
    }
    m_DelayerLock.WUnlock();
    return ErrorCode;
}

Uint32 CMkTimer::DeleteDelayer(const MkString& DelaySession)
{
    return DeleteTimer(DelaySession);
}

Uint32 CMkTimer::ActiveDelayer(const MkString& DelaySession)
{
    Uint32 ErrorCode = InvalidSession;
    m_DelayerLock.RLock();
    MkMap<MkString, MkDelayerParam>::iterator it = m_MapDelayer.find(DelaySession);
    if (it != m_MapDelayer.end()) {
        m_NextActiveTime = NowMkTime;
        it->second.ActiveTime = m_NextActiveTime;
        ErrorCode = NoneError;
    }
    m_DelayerLock.RUnlock();
    SetEvent(m_WaitEvent);
    return ErrorCode;
}

Uint32 CMkTimer::ActiveTimer(const MkString& TimerSession)
{
    return ActiveDelayer(TimerSession);
}

Uint32 CMkTimer::ChangeDelayerDelayTime(const MkString& DelaySession, Uint32 MilliSecDelay)
{
    Uint32 ErrorCode = InvalidSession;
    m_DelayerLock.RLock();
    MkMap<MkString, MkDelayerParam>::iterator it = m_MapDelayer.find(DelaySession);
    if (it != m_MapDelayer.end()) {
        it->second.MilliSecDelay = MilliSecDelay;
        if (it->second.ActiveTime > NowMkTime + MilliSecDelay * 1000) {
            it->second.ActiveTime = NowMkTime + MilliSecDelay * 1000;
        }
        ErrorCode = NoneError;
    }
    m_DelayerLock.RUnlock();
    SetEvent(m_WaitEvent);
    return ErrorCode;
}

Uint32 CMkTimer::ChangeTimerInterval(const MkString& TimerSession, Uint32 MilliSecInterval)
{
    return ChangeDelayerDelayTime(TimerSession, MilliSecInterval);
}

Uint32 CMkTimer::StartTimerThread()
{
    if (!m_bRun) {
        m_WaitThreadStartEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
        m_TimerThread.Start([this]() {
            Dispatch();
        });
        if (!m_bRun) {
            WaitForSingleObject(m_WaitThreadStartEvent, 0xFFFF);
        }
        CloseHandle(m_WaitThreadStartEvent);
        m_WaitThreadStartEvent = nullptr;
    }
    return NoneError;
}

void CMkTimer::LoopDelayer()
{
    CMkTime NowTime = NowMkTime;
    Uint32 ErrorCode = NoneError;
    MkVector<DelayerCallback> VecCb;
    if (NowTime >= m_NextActiveTime) {
        m_NextActiveTime = NowTime + 1000 * 1000 * 60; //one hour leater
        m_DelayerLock.WLock();
        MkMap<MkString, MkDelayerParam>::iterator it = m_MapDelayer.begin();
        while (it != m_MapDelayer.end()) {
            if (NowTime >= it->second.ActiveTime) {
                VecCb.push_back(it->second.Cb);
                if (!it->second.bTimer
                    || NoneError != ErrorCode) {
                    it = m_MapDelayer.erase(it);
                    continue;
                }
                it->second.ActiveTime = it->second.ActiveTime + it->second.MilliSecDelay * 1000;
            }
            if (m_NextActiveTime > it->second.ActiveTime) {
                m_NextActiveTime = it->second.ActiveTime;
            }
            it++;
        }
        m_DelayerLock.WUnlock();
        for (size_t i = 0; i < VecCb.size(); i++) {
            if (m_bHasDelete) {
                m_NextActiveTime = NowTime;
                m_bHasDelete = FALSE;
                break;
            }
            VecCb[i]();
        }
    }
}

void CMkTimer::AddWaitMilliSec(const CMkTime& NowTime, Uint32 WaitMillSec)
{
    m_WaitTimeLock.WLock();
    MkList<std::pair<CMkTime, Uint32>>::iterator it = m_WaitTimeList.begin();
    while (it != m_WaitTimeList.end()) {
        if (NowTime.GetTvSec() > it->first.GetTvSec() + 2) {
            it = m_WaitTimeList.erase(it);
        } else {
            it++;
        }
    }
    m_WaitTimeList.push_back(std::pair<CMkTime, Uint32>(NowTime, WaitMillSec));
    m_WaitTimeLock.WUnlock();
}

Uint32 CMkTimer::Dispatch()
{
    Uint64 WaitMillsec = 0xFFFFFFFF;
    CMkTime NowTime;
    MkString strTime;
    int ret = 0;
    m_bRun = TRUE;
    if (m_WaitThreadStartEvent) {
        SetEvent(m_WaitThreadStartEvent);
    }
    while (m_bRun) {
        //get wait time
        NowTime = NowMkTime;
        WaitMillsec = m_NextActiveTime > NowTime ? ((m_NextActiveTime - NowTime) / 1000) : 0;
        if (WaitMillsec) {
            AddWaitMilliSec(NowTime, static_cast<Uint32>(WaitMillsec));
            WaitForSingleObject(m_WaitEvent, static_cast<Uint32>(WaitMillsec));
            //if (WAIT_OBJECT_0 == ret) {
            //    MkDebugLog("wait success run:%d\n",m_bRun);
            //}
        }
        LoopDelayer();
    }
    m_DelayerLock.WLock();
    m_MapDelayer.clear();
    m_DelayerLock.WUnlock();
    CloseHandle(m_WaitEvent);
    m_WaitEvent = nullptr;
    return NoneError;
}

Uint32 CMkTimer::LoopBreak()
{
    m_bRun = FALSE;
    if (m_WaitEvent) {
        SetEvent(m_WaitEvent);
    }
    return NoneError;
}

Uint32 CMkTimer::GetScore()
{
    Uint32 Score = MaxUint32;
    CMkTime NowTime = NowMkTime;
    m_WaitTimeLock.RLock();
    MkList<std::pair<CMkTime, Uint32>>::iterator it = m_WaitTimeList.begin();
    for (; it != m_WaitTimeList.end(); it++) {
        if (it->first.GetTvSec() + 1 == NowTime.GetTvSec()) {
            Score += it->second;
        }
    }
    m_WaitTimeLock.RUnlock();
    Score -= m_MapDelayer.size();
    return Score;
}

CMkTimer::~CMkTimer()
{

}